package utils;

public class Complexe {
	private double real,im,module,argument;
	
	public static final Complexe ZERO=createComplexeFromRI(0,0);
	public static final Complexe INFINITY=createComplexeFromRI(Double.POSITIVE_INFINITY,0); 
	
	public static Complexe createComplexeFromRI(double r,double i){
		double mo=Math.sqrt(r*r+i*i); 
		double ar=Math.atan2(i,r); 	
		return new Complexe(r,i,mo,ar); 
	}
	
	public static Complexe intPower(Complexe z,int p){
		Complexe zt=Complexe.createComplexeFromRI(1,0); 
		for(int i=0;i<p;i++) zt=Complexe.mul(zt, z); 
		return zt; 
	}
	
	public static Complexe createComplexeFromPolar(double m,double a){
		double r=m*Math.cos(a); 
		double i=m*Math.sin(a); 
		return new Complexe(r,i,m,a);
	}
	
	private Complexe(double real, double im, double module, double argument) {
		super();
		this.real = real;
		this.im = im;
		this.module = module;
		this.argument = argument;
	}
	
	public Complexe(Complexe source){
		this.real=source.real; 
		this.im=source.im; 
		this. module=source.module; 
		this.argument=source.argument; 
	}
	
	public boolean isInfinite(){
		return (Math.abs(this.module)==Double.POSITIVE_INFINITY); 	
	}
	
	public static Complexe sum(Complexe z1,Complexe z2){
		double r,i; 
		r=z1.real+z2.real; 
		i=z1.im+z2.im; 
		return createComplexeFromRI(r,i); 
	}
	
	public static Complexe sub(Complexe z1,Complexe z2){
		double r,i; 
		r=z1.real-z2.real; 
		i=z1.im-z2.im; 
		return createComplexeFromRI(r,i); 
	}
	
	public static Complexe mul(Complexe z1,Complexe z2){
		double r=z1.real*z2.real-z1.im*z2.im; 
		double i=z1.im*z2.real+z2.im*z1.real; 
		return createComplexeFromRI(r, i); 
	}
	
	public static Complexe opp(Complexe z1){
		return createComplexeFromRI(-z1.getReal(),-z1.getIm()); 
	}
	
	public static Complexe div(Complexe z1,Complexe z2){
		double r=z1.real*z2.real+z1.im*z2.im; 
		double i=z1.im*z2.real-z2.im*z1.real;
		double ratio=z2.squareNorm();
		return createComplexeFromRI(r/ratio, i/ratio); 
	}
	
	public static Complexe mulbyDouble(Complexe z,double x){
		return createComplexeFromRI(z.real*x,z.im*x);
	}
	
	public double squareNorm(){
			return this.real*this.real+this.im*this.im;
	}
	
	public static Complexe log(Complexe z){
		return createComplexeFromRI(Math.log(z.module),z.argument); 
	}
	
	public static Complexe conjugate(Complexe z){
		return createComplexeFromRI(z.real, -z.im);
	}
	
	public Complexe conjugate(){
		return createComplexeFromRI(this.real, -this.im);
	}
	
	public static Complexe inverse(Complexe z){
		return Complexe.mulbyDouble(Complexe.conjugate(z), 1/z.squareNorm());
		
	}
	
	public static double distance(Complexe z1,Complexe z2){
		double d=(z1.real-z2.real)*(z1.real-z2.real)+(z1.im-z2.im)*(z1.im-z2.im); 
		return Math.sqrt(d); 
	}
	
	public static Complexe power(Complexe z1,Complexe z2){
		double re,im; 
		double inner=z2.im*Math.log(z1.module)+z2.real*z1.argument;
		double coef=Math.pow(z1.module,z2.real)*Math.exp(-z2.im*z1.argument); 
		re=Math.cos(inner)*coef; 
		im=Math.sin(inner)*coef; 
		return createComplexeFromRI(re,im); 
	}
	
	public static Complexe sqrt(Complexe z){
		return createComplexeFromPolar(Math.sqrt(z.module),z.argument/2);
	}

	public double getReal() {
		return real;
	}

	public void setReal(double real) {
		this.real = real;
	}

	public double getIm() {
		return im;
	}

	public void setIm(double im) {
		this.im = im;
	}

	public double getModule() {
		return module;
	}

	public void setModule(double module) {
		this.module = module;
	}

	public double getArgument() {
		return argument;
	}

	public void setArgument(double argument) {
		this.argument = argument;
	}
	
	public String toString(){
			return "("+real+","+im+")-("+module+","+argument/Math.PI*180+")"; 
	}

	

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final Complexe other = (Complexe) obj;
		if (Double.doubleToLongBits(im) != Double.doubleToLongBits(other.im))
			return false;
		if (Double.doubleToLongBits(real) != Double
				.doubleToLongBits(other.real))
			return false;
		return true;
	}

}
